package org.robotlegs.xxl.base 
{
	import flash.display.DisplayObject;
	import flash.display.DisplayObjectContainer;
	import flash.events.Event;
	import flash.utils.Dictionary;
	import flash.utils.getQualifiedClassName;
	import org.osflash.signals.natives.sets.DisplayObjectSignalSet;
	import org.robotlegs.base.ContextError;
	import org.robotlegs.core.IInjector;
	import org.robotlegs.core.IMediator;
	import org.robotlegs.core.IMediatorMap;
	import org.robotlegs.core.IReflector;
	
	/**
	 * 基于Robotlegs,用Signal替换了注册事件机制
	 * @author XXL
	 */
	public class MediatorMap extends ViewMapBase implements IMediatorMap 
	{
		protected var reflector:IReflector;
		
		protected var mediatorByView:Dictionary;
		protected var mappingConfigByView:Dictionary;
		protected var mappingConfigByViewClassName:Dictionary;
		
		public function MediatorMap(contextView:DisplayObjectContainer, injector:IInjector, reflector:IReflector) 
		{
			super(contextView, injector);
			
			this.reflector = reflector;
			
			mediatorByView = new Dictionary(true);
			mappingConfigByView = new Dictionary(true);
			mappingConfigByViewClassName = new Dictionary();
		}
		
		protected function createMediatorUsing(viewComponent:Object, viewClassName:String = "", config:MappingConfig = null):IMediator 
		{
			var mediator:IMediator = mediatorByView[viewComponent];
			if (mediator == null)
			{
				viewClassName ||= getQualifiedClassName(viewComponent);
				config ||= mappingConfigByViewClassName[viewClassName];
				if (config)
				{
					var clazz:Class;
					for each(clazz in config.typedViewClasses)
					{
						injector.mapValue(clazz, viewComponent);
					}
					mediator = injector.instantiate(config.mediatorClass);
					for each(clazz in config.typedViewClasses)
					{
						injector.unmap(clazz);
					}
					registerMediator(viewComponent, mediator);
				}
			}
			return mediator;
		}
		
		/* INTERFACE org.robotlegs.core.IMediatorMap */
		
		public function mapView(viewClassOrName:*, mediatorClass:Class, injectViewAs:* = null, autoCreate:Boolean = true, autoRemove:Boolean = true):void 
		{
			var viewClassName:String = reflector.getFQCN(viewClassOrName);
			
			if (mappingConfigByViewClassName[viewClassName] != null)
			{
				throw new ContextError(ContextError.E_MEDIATORMAP_OVR + " - " + mediatorClass);
			}
			if (reflector.classExtendsOrImplements(mediatorClass, IMediator) == false)
			{
				throw new ContextError(ContextError.E_MEDIATORMAP_NOIMPL + " - " + mediatorClass);
			}
			
			var config:MappingConfig = new MappingConfig();
			config.mediatorClass = mediatorClass;
			config.autoCreate = autoCreate;
			config.autoRemove = autoRemove;
			if (injectViewAs)
			{
				if (injectViewAs is Array)
				{
					config.typedViewClasses = (injectViewAs as Array).concat();
				}
				else if (injectViewAs is Class)
				{
					config.typedViewClasses = [injectViewAs];
				}
			}
			else if (viewClassOrName is Class)
			{
				config.typedViewClasses = [viewClassOrName];
			}
			mappingConfigByViewClassName[viewClassName] = config;
			
			if (autoCreate || autoRemove)
			{
				viewListenerCount++;
				if (viewListenerCount == 1)
				{
					addListeners();
				}
			}
			if (autoCreate && contextView && (viewClassName == getQualifiedClassName(contextView)))
			{
				createMediatorUsing(contextView, viewClassName, config);
			}
		}
		
		public function unmapView(viewClassOrName:*):void 
		{
			var viewClassName:String = reflector.getFQCN(viewClassOrName);
			var config:MappingConfig = mappingConfigByViewClassName[viewClassName];
			if (config && (config.autoCreate || config.autoRemove))
			{
				viewListenerCount--;
				if (viewListenerCount == 0)
				{
					removeListeners();
				}
			}
			delete mappingConfigByViewClassName[viewClassName];
		}
		
		public function createMediator(viewComponent:Object):IMediator 
		{
			return createMediatorUsing(viewComponent);
		}
		
		public function registerMediator(viewComponent:Object, mediator:IMediator):void 
		{
			var mediatorClass:Class = reflector.getClass(mediator);
			if (injector.hasMapping(mediatorClass))
			{
				injector.unmap(mediatorClass);
			}
			injector.mapValue(mediatorClass, mediator);
			mediatorByView[viewComponent] = mediator;
			mappingConfigByView[viewComponent] = mappingConfigByViewClassName[getQualifiedClassName(viewComponent)];
			mediator.setViewComponent(viewComponent);
			mediator.preRegister();
		}
		
		public function removeMediator(mediator:IMediator):IMediator 
		{
			if (mediator)
			{
				var viewComponent:Object = mediator.getViewComponent();
				var mediatorClass:Class = reflector.getClass(mediator);
				delete mediatorByView[viewComponent];
				delete mappingConfigByView[viewComponent];
				mediator.preRemove();
				mediator.setViewComponent(null);
				if (injector.hasMapping(mediatorClass))
				{
					injector.unmap(mediatorClass);
				}
			}
			return mediator;
		}
		
		public function removeMediatorByView(viewComponent:Object):IMediator 
		{
			return removeMediator(retrieveMediator(viewComponent));
		}
		
		public function retrieveMediator(viewComponent:Object):IMediator 
		{
			return mediatorByView[viewComponent];
		}
		
		public function hasMapping(viewClassOrName:*):Boolean 
		{
			var viewClassName:String = reflector.getFQCN(viewClassOrName);
			return (mappingConfigByViewClassName[viewClassName] != null);
		}
		
		public function hasMediator(mediator:IMediator):Boolean 
		{
			for each(var med:IMediator in mediatorByView)
			{
				if (med == mediator)
				{
					return true;
				}
			}
			return false;
		}
		
		public function hasMediatorForView(viewComponent:Object):Boolean 
		{
			return mediatorByView[viewComponent] != null;
		}
		
		protected var signalSet:DisplayObjectSignalSet;
		override protected function addListeners():void 
		{
			if (contextView && enabled)
			{
				if (signalSet == null)
				{
					signalSet = new DisplayObjectSignalSet(contextView);
				}
				signalSet.addedToStage.add(onViewAdded);
				signalSet.added.add(onViewAdded);
				signalSet.removedFromStage.add(onViewRemoved);
				signalSet.removed.add(onViewRemoved);
			}
		}
		
		override protected function removeListeners():void 
		{
			if (signalSet)
			{
				signalSet.removeAll();
			}
		}
		
		protected function onViewAdded(e:Event):void 
		{
			var viewClassName:String = getQualifiedClassName(e.target);
			var config:MappingConfig = mappingConfigByViewClassName[viewClassName];
			if (config && config.autoCreate)
			{
				createMediatorUsing(e.target, viewClassName, config);
			}
		}
		
		protected function onViewRemoved(e:Event):void 
		{
			var config:MappingConfig = mappingConfigByView[e.target];
			if (config && config.autoRemove)
			{
				removeMediatorByView(e.target);
			}
		}
	}

}

class MappingConfig
{
	public var mediatorClass:Class;
	public var typedViewClasses:Array;
	public var autoCreate:Boolean;
	public var autoRemove:Boolean;
}